﻿using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace WebAPI_Demo.Filter
{
    /// <summary>
    /// 命名规则限流 方法过滤器
    ///    功能  过滤器类型 Filter
    /// </summary>
    public class RateLimitActionFilter : IActionFilter
    {

        //使用缓存记录时间 内存缓存的服务
        //1、添加服务 
        //2、获取服务
        private readonly IMemoryCache memCache;
        /// <summary>
        /// 限流过滤器的构造函数  实现构造注入
        /// </summary>
        /// <param name="memCache"></param>
        public RateLimitActionFilter(IMemoryCache memCache)
        {
            this.memCache = memCache;
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            //LogHelper.Info("OnActionExecuted");
            //执行方法后执行这
        }
        // 限流器 原理
        // 在请求方法前记录下IP请求的时间 在每次请求时 判断这个IP有没有已经被记录下的时间（上一次请求的时间）
        // 两次时间相减 如果少于1秒 不让他请求

        /// <summary>
        /// 执行接口方法之前
        /// </summary>
        /// <param name="context"></param>
        public void OnActionExecuting(ActionExecutingContext context)
        {
            Console.WriteLine("RateLimitActionFilter 执行action前");
            //获取请求IP
            string ip = context.HttpContext.Connection.RemoteIpAddress.ToString();
            //缓存key  key value
            string cacheKey = $"lastvisittick_{ip}";
            //  上一次请求时间 获取这个IP有没有已经记录下来时间 
            //long? 可能是空的 如果是第一次请求 就没有上一次请求时间
            long? lastVisit = memCache.Get<long?>(cacheKey);
            // 如果第一请求    或则    （当前的系统时间毫秒-上一次请求时间）两次请求的时间间隔大于 1000毫秒
            if (lastVisit == null || Environment.TickCount64 - lastVisit > 1000) // Environment.TickCount64 系统时间
            {
                //可以请求的
                //记录下来这次请求时间
                memCache.Set(cacheKey, Environment.TickCount64, TimeSpan.FromSeconds(10));//避免长期不访问的用户，占据缓存的内存
                Console.WriteLine("RateLimitActionFilter 执行action后");
            }
            //否则不让他请求
            else
            {
                //直接返回一个结果 这样就不会执行方法里面代码
                ObjectResult result = new ObjectResult("访问太频繁")
                {
                    StatusCode = 429
                };
                context.Result = result;
            }
        }
    }
}
